package service

import (
	v1 "SmartLibrary/api/v1"
	"SmartLibrary/internal/model"
	"SmartLibrary/internal/repository"
	"SmartLibrary/pkg/chat"
	"context"
	"encoding/json"
	"fmt"
	"github.com/gorilla/websocket"
	"go.uber.org/zap"
	"golang.org/x/crypto/bcrypt"
	"io"
	"time"
)

type UserService interface {
	Login(ctx context.Context, req *v1.LoginRequest) (string, error)
	GetProfile(ctx context.Context, userId int64) (*v1.GetProfileResponseData, error)
	GetBooks(ctx context.Context, req *v1.GetBooksCondition) (*v1.GetBooksResponse, error)
	GetRules(ctx context.Context) (*v1.GetRulesResponse, error)
	GetNotices(ctx context.Context) (*[]model.UserNotice, error)
	GetBorrows(ctx context.Context, req *v1.GetBorAndVioCondition) (*v1.GetBorrowsResponse, error)
	GetViolations(ctx context.Context, req *v1.GetBorAndVioCondition) (*v1.GetViolationsResponse, error)
	GetComments(ctx context.Context) (*[]model.UserComment, error)
	CreateComment(ctx context.Context, req *v1.CreateCommentRequest) error
	GetChats(params *v1.ChatParams, ws *websocket.Conn) error
}

func NewUserService(
	service *Service,
	userRepo repository.UserRepository,
	chat *chat.Chat,
) UserService {
	return &userService{
		userRepo: userRepo,
		chat:     chat,
		Service:  service,
	}
}

type userService struct {
	userRepo repository.UserRepository
	chat     *chat.Chat
	*Service
}

func (s *userService) Login(ctx context.Context, req *v1.LoginRequest) (string, error) {
	user, err := s.userRepo.GetByUsername(ctx, req.UserName)
	if err != nil {
		if err == v1.ErrUsernameOrPasswordWrong {
			return "", err
		}
		s.logger.Error(fmt.Sprintf("failed to get user by username when user login, username: %s", req.UserName), zap.Error(err))
		return "", v1.ErrBadRequest
	}

	// 如果密码正确
	err = bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(req.Password))
	if err != nil {
		return "", v1.ErrBadRequest
	}
	// 生成 token
	token, err := s.jwt.GenToken(user.UserID, time.Now().Add(time.Hour*24*90))
	if err != nil {
		return "", v1.ErrBadRequest
	}

	return token, nil
}

func (s *userService) GetProfile(ctx context.Context, userId int64) (*v1.GetProfileResponseData, error) {
	user, err := s.userRepo.GetByID(ctx, userId)
	if err != nil {
		if err == v1.ErrUserIdNotFound {
			return nil, err
		}
		s.logger.Error(fmt.Sprintf("failed to get user profile by id, userId: %d", userId), zap.Error(err))
		return nil, v1.ErrBadRequest
	}

	return &v1.GetProfileResponseData{
		UserId:     user.UserID,
		UserName:   user.Username,
		CardName:   user.CardName,
		CardNumber: user.CardNumber,
		RuleNumber: user.RuleNumber,
		Status:     user.Status,
	}, nil
}

func (s *userService) GetBooks(ctx context.Context, req *v1.GetBooksCondition) (*v1.GetBooksResponse, error) {
	if req.Query == "" || req.Condition == "" {
		// 无条件查总数量
		total, err := s.userRepo.GetBookAll(ctx)
		if err != nil {
			s.logger.Error("failed to get books all", zap.Error(err))
			return nil, v1.ErrBadRequest
		}
		if total == 0 {
			return nil, v1.ErrBookNotFound
		}
		// 无条件分页查数据
		books, err := s.userRepo.GetBookByPage(ctx, req)
		if err != nil {
			s.logger.Error(fmt.Sprintf("failed to get books by page, Offset: %d, Limit: %d", req.Offset, req.Limit), zap.Error(err))
			return nil, v1.ErrBadRequest
		}

		return &v1.GetBooksResponse{
			Records: books,
			Total:   total,
		}, nil
	}

	// 有条件查总数量
	total, err := s.userRepo.GetBookAllByCondition(ctx, req)
	if err != nil {
		s.logger.Error(fmt.Sprintf("failed to get books all by condition, condition: %+v", req), zap.Error(err))
		return nil, v1.ErrBadRequest
	}
	if total == 0 {
		return nil, v1.ErrBookNotFound
	}

	// 有条件查数据
	books, err := s.userRepo.GetBookByCondition(ctx, req)
	if err != nil {
		s.logger.Error(fmt.Sprintf("failed to get books by condition, condition: %+v", req), zap.Error(err))
		return nil, v1.ErrBadRequest
	}

	return &v1.GetBooksResponse{
		Records: books,
		Total:   total,
	}, nil
}

func (s *userService) GetRules(ctx context.Context) (*v1.GetRulesResponse, error) {
	rules, err := s.userRepo.GetBookRules(ctx)
	if err != nil {
		s.logger.Error("failed to get book rules", zap.Error(err))
		return nil, v1.ErrBadRequest
	}

	return &v1.GetRulesResponse{
		Records: rules,
		Total:   len(*rules),
	}, nil
}

func (s *userService) GetNotices(ctx context.Context) (*[]model.UserNotice, error) {
	notices, err := s.userRepo.GetNotices(ctx)
	if err != nil {
		s.logger.Error("failed to get notices", zap.Error(err))
		return nil, v1.ErrBadRequest
	}

	return notices, nil
}

func (s *userService) GetBorrows(ctx context.Context, req *v1.GetBorAndVioCondition) (*v1.GetBorrowsResponse, error) {
	cardNumber, err := s.userRepo.GetCardNumberByUserId(ctx, req.UserId)
	if err != nil {
		s.logger.Error(fmt.Sprintf("failed to get card number by user id, userId: %d", req.UserId), zap.Error(err))
		return nil, v1.ErrBadRequest
	}
	req.CardNumber = cardNumber

	if req.Query == "" || req.Condition == "" {
		// 无条件查总数量
		total, err := s.userRepo.GetBorrowAll(ctx, cardNumber)
		if err != nil {
			s.logger.Error(fmt.Sprintf("failed to get borrows all, cardNumber: %d", cardNumber), zap.Error(err))
			return nil, v1.ErrBadRequest
		}
		if total == 0 {
			return nil, v1.ErrBorrowNotFound
		}
		// 无条件分页查数据
		borrows, err := s.userRepo.GetBorrowByCardNumber(ctx, req)
		if err != nil {
			s.logger.Error(fmt.Sprintf("failed to get borrows by card number, cardNumber: %d", cardNumber), zap.Error(err))
			return nil, v1.ErrBadRequest
		}

		// 整合数据返回
		return &v1.GetBorrowsResponse{
			Records: borrows,
			Total:   total,
		}, nil
	}

	// 查符合条件总条数
	total, err := s.userRepo.GetBorrowAllByCondition(ctx, req)
	if err != nil {
		s.logger.Error(fmt.Sprintf("failed to get borrows all by condition, condition: %+v", req), zap.Error(err))
		return nil, v1.ErrBadRequest
	}
	if total == 0 {
		return nil, v1.ErrBorrowNotFound
	}
	// 查符合条件分页查询
	borrows, err := s.userRepo.GetBorrowByCondition(ctx, req)
	if err != nil {
		s.logger.Error(fmt.Sprintf("failed to get borrows by condition, condition: %+v", req), zap.Error(err))
		return nil, v1.ErrBadRequest
	}

	return &v1.GetBorrowsResponse{
		Records: borrows,
		Total:   total,
	}, nil
}

func (s *userService) GetViolations(ctx context.Context, req *v1.GetBorAndVioCondition) (*v1.GetViolationsResponse, error) {
	cardNumber, err := s.userRepo.GetCardNumberByUserId(ctx, req.UserId)
	if err != nil {
		s.logger.Error(fmt.Sprintf("failed to get card number by user id, userId: %d", req.UserId), zap.Error(err))
		return nil, v1.ErrBadRequest
	}
	req.CardNumber = cardNumber

	if req.Query == "" || req.Condition == "" {
		// 无条件查总数量
		total, err := s.userRepo.GetViolationAll(ctx, cardNumber)
		if err != nil {
			s.logger.Error(fmt.Sprintf("failed to get violations all, cardNumber: %d", cardNumber), zap.Error(err))
			return nil, v1.ErrBadRequest
		}
		if total == 0 {
			return nil, v1.ErrViolationNotFound
		}

		// 无条件分页查询
		violations, err := s.userRepo.GetViolationsByCardNumber(ctx, req)
		if err != nil {
			s.logger.Error(fmt.Sprintf("failed to get violations by card number, cardNumber: %d", cardNumber), zap.Error(err))
			return nil, v1.ErrBadRequest
		}

		// 整合数据返回
		return &v1.GetViolationsResponse{
			Records: violations,
			Total:   total,
		}, nil
	}

	// 有条件查总数量
	total, err := s.userRepo.GetViolationAllByCondition(ctx, req)
	if err != nil {
		s.logger.Error(fmt.Sprintf("failed to get violations all by condition, condition: %+v", req), zap.Error(err))
		return nil, v1.ErrBadRequest
	}
	if total == 0 {
		return nil, v1.ErrBookNotFound
	}
	// 有条件查数据
	violations, err := s.userRepo.GetViolationByCondition(ctx, req)
	if err != nil {
		s.logger.Error(fmt.Sprintf("failed to get violations by condition, condition: %+v", req), zap.Error(err))
		return nil, v1.ErrBadRequest
	}

	return &v1.GetViolationsResponse{
		Records: violations,
		Total:   total,
	}, nil
}

func (s *userService) GetComments(ctx context.Context) (*[]model.UserComment, error) {
	comments, err := s.userRepo.GetComments(ctx)
	if err != nil {
		s.logger.Error("failed to get comments", zap.Error(err))
		return nil, v1.ErrBadRequest
	}

	return comments, nil
}

func (s *userService) CreateComment(ctx context.Context, req *v1.CreateCommentRequest) error {
	req.CreateTime = time.Now()
	err := s.userRepo.CreateComment(ctx, req)
	if err != nil {
		s.logger.Error(fmt.Sprintf("failed to create comment, userId: %d", req.UserId), zap.Error(err))
		return v1.ErrBadRequest
	}

	return nil
}

func (s *userService) GetChats(params *v1.ChatParams, ws *websocket.Conn) error {
	d := websocket.Dialer{
		HandshakeTimeout: 5 * time.Second,
	}

	// 握手并建立 websocket 连接
	conn, resp, err := d.Dial(s.chat.GenerateURL(), nil)
	if err != nil || resp.StatusCode != 101 {
		s.logger.Error(fmt.Sprintf("failed to dial chat, userId: %d", params.UserID), zap.Error(err))

		// 读取响应体
		body, _ := io.ReadAll(resp.Body)
		// 记录响应体相关内容
		s.logger.Info("Error Response Message:", zap.Any("Status", resp.Status), zap.Any("Code", resp.StatusCode), zap.Any("Body", string(body)))

		return v1.ErrBadRequest
	}
	defer func(conn *websocket.Conn) {
		err := conn.Close()
		if err != nil {
			s.logger.Error(fmt.Sprintf("failed to close chat, userId: %d", params.UserID), zap.Error(err))
		}
	}(conn)

	// 传递需要的信息
	contents := []v1.ChatConditions{
		{
			Role:    "system",
			Content: "你现在要作为一个图书管理员，我会输入偏好信息，你需要根据这些信息推荐出相应的书籍",
		},
		{
			Role:    "user",
			Content: params.Message,
		},
	}

	// 发送数据
	data := s.chat.GenerateParams(&contents)
	err = conn.WriteJSON(data)
	if err != nil {
		s.logger.Error(fmt.Sprintf("failed to write message, userId: %d", params.UserID), zap.Error(err))
		return v1.ErrBadRequest
	}

	// 不断读取数据
	for {
		_, msg, err := conn.ReadMessage()
		if err != nil {
			s.logger.Error(fmt.Sprintf("failed to read message, userId: %d", params.UserID), zap.Error(err))
			return v1.ErrBadRequest
		}

		var data map[string]interface{}
		err = json.Unmarshal(msg, &data)
		if err != nil {
			s.logger.Error(fmt.Sprintf("failed to unmarshal message, userId: %d", params.UserID), zap.Error(err))
			return v1.ErrBadRequest
		}

		// 解析数据
		payload := data["payload"].(map[string]interface{})
		choices := payload["choices"].(map[string]interface{})
		header := data["header"].(map[string]interface{})
		// interface{} 类型的数值默认为 float64 类型
		code := header["code"].(float64)

		if code != 0 {
			s.logger.Error(fmt.Sprintf("failed to get message, userId: %d", params.UserID), zap.Error(err))
			return v1.ErrBadRequest
		}

		status := choices["status"].(float64)
		text := choices["text"].([]interface{})
		content := text[0].(map[string]interface{})["content"].(string)

		// 立即将消息发送回 websocket 客户端
		err = ws.WriteMessage(websocket.TextMessage, []byte(content))
		if err != nil {
			s.logger.Error(fmt.Sprintf("failed to write message to websocket, userId: %d", params.UserID), zap.Error(err))
			return v1.ErrBadRequest
		}

		if status == 2 {
			_ = ws.WriteJSON(map[string]interface{}{
				"message": "end",
			})
			usage := payload["usage"].(map[string]interface{})
			temp := usage["text"].(map[string]interface{})
			totalTokens := temp["total_tokens"].(float64)
			fmt.Println("total_tokens:", totalTokens)
			return nil
		}
	}

}
